home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1995…tember: Reference Library / Dev.CD Sep 95 RL / Dev.CD Sep 95 RL.toast / mac / Technical Documentation / develop / develop Issue 22 code / PCI Driver Sample / NCR_MiniTest / MiniDriverTest.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-07-27  |  24.5 KB  |  979 lines  |  [TEXT/MPCC]

  1. /*                                    MiniDriverTest.c                                */
  2. /*
  3.  * MiniDriverTest.c
  4.  * Copyright © 1994 Apple Computer Inc. All Rights Reserved.
  5.  */
  6. /*    .___________________________________________________________________________________.
  7.       | This is a very simple test program for the PCI sample driver. It has not been        |
  8.     | compiled for, or tested in, 68000 emulation mode. It is also pretty ugly: it was    |
  9.     | written quickly, and while I was debugging the driver. It is not a prize example    |
  10.     | of production software.                                                            |
  11.     .___________________________________________________________________________________.
  12.  */
  13. #define TEST_DRIVER                1
  14. #include <NCRDriverPrivate.h>
  15. #include <Devices.h>
  16. #include <Fonts.h>
  17. #include <ToolUtils.h>
  18. #include <SegLoad.h>
  19. #include <SCSI.h>
  20. #include "MacSCSICommand.h"
  21. #include "LogLibrary.h"
  22.  
  23. #ifndef FALSE
  24. #define FALSE                0
  25. #define TRUE                1
  26. #endif
  27. /*
  28.  * Edit this to define the device to test.
  29.  */
  30. #define kFirstSCSITargetID    2
  31. #define kMaxSCSITargetID    2
  32. #define kSCSITrials            1000
  33. #define kISRTrials            1
  34. #define kFirstBlock            1000
  35. #define kLastBlock            100
  36. /*
  37.  * if kMinBlocks >kMaxBlocks, the transfer length will change with each request.
  38.  */
  39. #define kMinBlocks            1            /* Must be at least 1                */
  40. /*
  41.  * Because we use a 6-byte read command, kMaxBlocks may not exceed 128
  42.  */
  43. #define kMaxBlocks            64            /* Should be >= kMinBlocks            */
  44. #define kTimeout            2000L
  45. #define kReadBufferSize        2048        /* Big enough for CD-ROMs            */
  46.  
  47. short                        gDriverRefNum;
  48. short                        gSCSITargetID;
  49. short                        gSCSITargetLUN = 0;
  50. short                        gTrials;
  51. Boolean                        gQuitNow;
  52. LogRecordPtr                gLogRecordPtr;
  53. MenuHandle                    gAppleMenu;
  54. MenuHandle                    gFileMenu;
  55. EventRecord                    gEventRecord;
  56. Boolean                        gInForeground;
  57. IOParam                        gIOParam;
  58. NCRSCSIParam                gNCRSCSIParam;
  59. Ptr                            gReadBufferPtr;
  60. unsigned long                gReadBufferSize;
  61. unsigned long                gCurrentBlockLength;
  62. unsigned long                gDeviceSize;
  63.  
  64. void                        DoEventLoop(void);
  65. void                        DoMouseEvent(void);
  66. void                        DoCommand(
  67.         long                    menuChoice
  68.     );
  69. #define MENU_Apple            1
  70. #define MENU_File            256
  71. #define kAppleAbout            1
  72. #define kFileQuit            1
  73.  
  74. /*
  75.  * SCSI commands
  76.  */
  77. void                        DoDeviceTest(void);
  78. void                        DoISRTest(void);
  79. OSErr                        DoOneISRTest(void);
  80. OSErr                        DoBusReset(void);
  81. OSErr                        DoTestUnitReady(void);
  82. OSErr                        DoDeviceInquiry(void);
  83. OSErr                        DoReadCapacity(void);
  84. OSErr                        DoReadAllBlocks(void);
  85. OSErr                        DoReadBlock(
  86.         unsigned long            blockNumber,
  87.         unsigned long            nBlocks,
  88.         Boolean                    dumpBlock
  89.     );
  90. Boolean                        DoRequestSense(            /* TRUE if Unit Attention        */
  91.         unsigned short            targetID,            /* SCSI Bus ID                    */
  92.         unsigned short            targetLUN            /* SCSI LUN -- not supported    */
  93.     );
  94. OSErr                        DoIORundown(void);
  95. unsigned long                SCSIGetCommandLength(
  96.         const unsigned char        *cmdBlock
  97.     );
  98. Boolean                        ShowRequestSense(        /* TRUE if Unit Attention        */
  99.         unsigned short            targetID,            /* SCSI Bus ID                    */
  100.         unsigned short            targetLUN,            /* SCSI LUN -- not supported    */
  101.         OSErr                    callStatus,            /* DoDriverIO Request Sense        */
  102.         const SCSI_Sense_Data    *sense,
  103.         unsigned long            actualTransferCount
  104.     );
  105. #define AppendChar(result, theChar)    do {                \
  106.         StringPtr        _dst = (result);                \
  107.         _dst[++_dst[0]] = theChar;                        \
  108.     } while (0)
  109. void                        ShowMemory(
  110.         const Ptr                memStart,
  111.         unsigned long            byteCount
  112.     );
  113.  
  114. void                        AppendHexLeadingZeros(
  115.         StringPtr                result,
  116.         unsigned long            value,
  117.         short                    fieldWidth
  118.     );
  119. void                        AppendSigned(
  120.         StringPtr                result,
  121.         signed long                value
  122.     );
  123. void                        AppendUnsigned(
  124.         StringPtr                result,
  125.         unsigned long            value
  126.     );
  127.  
  128. /*
  129.  * NCR Driver commands
  130.  */
  131. OSErr                        DoNCRDriverIOWithSense(
  132.         unsigned short            driverAction,        /* Input, output, or nothing    */
  133.         unsigned short            targetID,            /* SCSI Bus ID                    */
  134.         unsigned short            targetLUN,            /* SCSI LUN -- not supported    */
  135.         const unsigned char        *scsiCommand,        /* SCSI Command itself            */
  136.         Ptr                        dataBufferPtr,        /* User data buffer or NULL     */
  137.         unsigned long            transferCount,
  138.         unsigned long            *actualTransferCount
  139.     );
  140. OSErr                        DoNCRDriverIO(
  141.         unsigned short            driverAction,        /* Input, output, or nothing    */
  142.         unsigned short            targetID,            /* SCSI Bus ID                    */
  143.         unsigned short            targetLUN,            /* SCSI LUN -- not supported    */
  144.         const unsigned char        *scsiCommand,        /* SCSI Command itself            */
  145.         Ptr                        dataBufferPtr,        /* User data buffer or NULL     */
  146.         unsigned long            transferCount,
  147.         unsigned long            *actualTransferCount
  148.     );
  149. /*
  150.  * A handy macro to clear a (small) structure.
  151.  */
  152. void                        ClearMemory(
  153.         register Ptr            memPtr,
  154.         register Size            memSize
  155.     );
  156. #undef CLEAR
  157. #define CLEAR(what)    ClearMemory((Ptr) &what, sizeof what)
  158.  
  159. #define ShowString(string) WriteLogEntry(gLogRecordPtr, 'Test', LogStringFormat, (string))
  160. #define ShowStatusString(status, string)                     \
  161.     WriteLogEntry(gLogRecordPtr, 'Test',                    \
  162.         LogFormat2(kLogFormatSigned, kLogFormatString),        \
  163.         (status), (string))
  164. #define ShowHex(value, string)                                \
  165.     WriteLogEntry(                                            \
  166.         gLogRecordPtr, 'Test',                                \
  167.         LogFormat2(kLogFormatAddress, kLogFormatString),    \
  168.         (unsigned long) (value),                            \
  169.         (string)                                            \
  170.     )
  171. #define ShowDecimal(value, string)                            \
  172.     WriteLogEntry(                                            \
  173.         gLogRecordPtr, 'Test',                                \
  174.         LogFormat2(kLogFormatSigned, kLogFormatString),        \
  175.         (signed long) (value),                                \
  176.         (string)                                            \
  177.     )
  178.  
  179. void
  180. main(void)
  181. {
  182.         OSErr                status;
  183.         short                i;
  184.         
  185.         gLogRecordPtr = MakeLogRecord(kPCIDeviceNameCString, 256);
  186.         MaxApplZone();
  187.         InitGraf(&qd.thePort);
  188.         InitFonts();
  189.         InitWindows();
  190.         InitMenus();
  191.         TEInit();
  192.         InitDialogs(0);
  193.         for (i = 0; i < 8; i++)
  194.             MoreMasters();
  195.         HNoPurge((Handle) GetCursor(watchCursor));
  196.         SetCursor(*GetCursor(watchCursor));
  197.         gReadBufferSize = kReadBufferSize * kMaxBlocks;
  198.         gReadBufferPtr = NewPtrClear(gReadBufferSize);
  199.         if (gReadBufferPtr == NULL) {
  200.             ShowString("\pNo memory for read buffer");
  201.             ExitToShell();
  202.         }
  203.         gAppleMenu = NewMenu(MENU_Apple, "\p\024");
  204.         AppendMenu(gAppleMenu, "\p(No About;(-");
  205.         AppendResMenu(gAppleMenu, 'DRVR');
  206.         gFileMenu = NewMenu(MENU_File, "\pFile");
  207.         AppendMenu(gFileMenu, "\pQuit/Q");
  208.         InsertMenu(gAppleMenu, 0);
  209.         InsertMenu(gFileMenu, 0);
  210.         DrawMenuBar();
  211.         status = OpenDriver(kDriverNamePString, &gDriverRefNum);
  212.         if (status != noErr) {
  213.             ShowStatusString(status, "\pCan't open driver");
  214.             ExitToShell();
  215.         }
  216.         DoDeviceTest();
  217.         ExitToShell();
  218. }
  219.  
  220. void
  221. DoDeviceTest(void)
  222. {
  223.         OSErr                status;
  224.         unsigned long        startBlock;
  225.         unsigned long        nBlocks;
  226.  
  227.         status = DoBusReset();
  228.         if (status != noErr)
  229.             ShowStatusString(status, "\pDo Bus Reset failed");
  230.         else {
  231.             for (gTrials = 0;
  232.                         gQuitNow == FALSE
  233.                         && (kSCSITrials == 0 || gTrials < kSCSITrials);
  234.                         gTrials++) {
  235.                 ShowDecimal(gTrials, "\pTrial");
  236.                 for (gSCSITargetID = kFirstSCSITargetID;
  237.                             gQuitNow == FALSE && gSCSITargetID <= kMaxSCSITargetID;
  238.                             gSCSITargetID++) {
  239.                     DoEventLoop();
  240.                     if (gQuitNow == FALSE) {
  241.                         ShowDecimal(gSCSITargetID, "\pTarget");
  242.                         status = DoTestUnitReady();
  243.                         DoEventLoop();
  244.                         if (status == noErr)
  245.                             status = DoDeviceInquiry();
  246.                         DoEventLoop();
  247.                         if (status == noErr)
  248.                             status = DoReadCapacity();
  249.                         DoEventLoop();
  250.                         ShowStatusString(status, "\pDoDeviceTest after init");
  251.                         if (1 && status == noErr) {        /* Turn on for device exercise    */
  252.                             nBlocks = kMinBlocks;
  253.                             for (startBlock = 0;
  254.                                     startBlock < (gDeviceSize - kMaxBlocks);
  255.                                     startBlock++) {
  256.                                 if (nBlocks > kMaxBlocks)
  257.                                     nBlocks = kMinBlocks;
  258.                                 DoEventLoop();
  259.                                 if (DoReadBlock(startBlock, nBlocks, FALSE) != noErr)
  260.                                     break;
  261.                                 ++nBlocks;
  262.                                 if (gQuitNow) {
  263.                                     ShowString("\pQuit requested");
  264.                                     break;
  265.                                 }
  266.                             }
  267.                         }
  268.                     }
  269.                 }
  270.             }
  271.         }
  272.         ShowStatusString(status, "\pDoDeviceTest exits");
  273. }
  274.  
  275. void
  276. DoISRTest(void)
  277. {
  278.         OSErr                        status;
  279.         
  280.         for (status = noErr, gTrials = 0;
  281.                     kISRTrials == 0 || gTrials < kISRTrials;
  282.                     gTrials++) {
  283.             DoEventLoop();
  284.             ShowDecimal(gTrials, "\pISR Trial");
  285.             status = DoOneISRTest();
  286.             if (status != noErr) {
  287.                 ShowStatusString(status, "\pISR Test failed");
  288.                 gQuitNow = TRUE;
  289.                 break;
  290.             }
  291.         }
  292. }
  293.  
  294. OSErr
  295. DoOneISRTest(void)
  296. {
  297.         OSErr                    status;
  298.         unsigned char            dummyBuffer;
  299. #define NCR    (gNCRSCSIParam)
  300. #define PB    (gIOParam)
  301.  
  302.         status = noErr;
  303.         /*
  304.          * Setup the parameter block
  305.          */
  306.         CLEAR(PB);
  307.         CLEAR(NCR);
  308.         PB.ioRefNum = gDriverRefNum;
  309.         NCR.driverAction = kNCRDriverNoDataPhase;
  310.         NCR.targetID = kNCRMemoryTestBusID;
  311.         NCR.watchdogTimeout = kTimeout;        /* Presumably a two second timeout    */
  312.         PB.ioBuffer = (Ptr) &dummyBuffer;
  313.         PB.ioReqCount = 1;
  314.         PB.ioMisc = (Ptr) &gNCRSCSIParam;
  315.         status = PBReadSync((ParmBlkPtr) &PB);
  316.         return ((status != noErr) ? status : PB.ioResult);
  317. #undef PB
  318. #undef NCR
  319. }
  320.  
  321. void
  322. DoEventLoop(void)
  323. {
  324.         long                            menuChoice;
  325.         register WindowPtr                theWindow;
  326.         GrafPtr                            savePort;
  327.         Boolean                            isActivating;
  328.         
  329.         WaitNextEvent(
  330.             everyEvent,
  331.             &gEventRecord,
  332.             10L,
  333.             NULL
  334.         );
  335.         theWindow = FrontWindow();
  336.         switch (gEventRecord.what) {
  337.         case nullEvent:
  338.             break;
  339.         case keyDown:
  340.         case autoKey:
  341.             if ((gEventRecord.message & charCodeMask) == '.'
  342.              && (gEventRecord.modifiers & cmdKey) != 0) {
  343.                 FlushEvents(keyDown | autoKey, 0);
  344.                 gQuitNow = TRUE;
  345.             }
  346.             else if ((gEventRecord.modifiers & cmdKey) != 0) {
  347.                 if (gEventRecord.what == keyDown) {
  348.                     menuChoice = MenuKey(gEventRecord.message & charCodeMask);
  349.                     if (HiWord(menuChoice) != 0)
  350.                         DoCommand(menuChoice);
  351.                 }
  352.             }
  353.             break;
  354.         case mouseDown:
  355.             DoMouseEvent();
  356.             break;
  357.         case updateEvt:
  358.             theWindow = (WindowPtr) gEventRecord.message;
  359.             GetPort(&savePort);
  360.             SetPort(theWindow);
  361.             BeginUpdate(theWindow);
  362.             EraseRect(&theWindow->portRect);
  363.             DrawControls(theWindow);
  364.             DrawGrowIcon(theWindow);
  365.             EndUpdate(theWindow);
  366.             SetPort(savePort);
  367.             break;
  368.         case activateEvt:
  369.             theWindow = (WindowPtr) gEventRecord.message;
  370.             isActivating = ((gEventRecord.modifiers & activeFlag) != 0);
  371.             goto activateEvent;
  372.             break;
  373.         case osEvt:
  374.             switch (((unsigned long) gEventRecord.message) >> 24) {
  375.             case mouseMovedMessage:
  376.                 break;
  377.             case suspendResumeMessage:
  378.                 isActivating = ((gEventRecord.message & 0x01) != 0);
  379. activateEvent:        if (isActivating) {
  380.                     /*
  381.                      * Activate this window. Activate events define theWindow
  382.                      * from the event record, while suspend/resume uses the
  383.                      * pre-set FrontWindow value.
  384.                      */
  385.                     SelectWindow(theWindow);
  386.                     (void) TEFromScrap();
  387.                 }
  388.                 gInForeground = isActivating;
  389.                 break;
  390.             }
  391.             break;
  392.         }
  393. }
  394.  
  395.  
  396. /*
  397.  * DoMouseEvent
  398.  * The user clicked on something. Handle application-wide processing here, or call
  399.  * a Catalog Browser function for specific action.
  400.  */
  401. void
  402. DoMouseEvent(void)
  403. {
  404.         WindowPtr        theWindow;
  405.         short            whichPart;
  406.         
  407.         whichPart = FindWindow(gEventRecord.where, &theWindow);
  408.         switch (whichPart) {
  409.         case inMenuBar:
  410.             InitCursor();
  411.             DoCommand(MenuSelect(gEventRecord.where));
  412.             break;
  413.         }
  414. }
  415.  
  416. void
  417. DoCommand(
  418.         long                    menuChoice
  419.     )
  420. {
  421.         short                    menuItem;
  422.         Str255                    menuText;
  423.         GrafPtr                    savePort;
  424.  
  425.         menuItem = LoWord(menuChoice);
  426.         switch (HiWord(menuChoice)) {
  427.         case MENU_Apple:
  428.             if (menuItem != kAppleAbout) {
  429.                 GetMenuItemText(gAppleMenu, menuItem, menuText);
  430.                 GetPort(&savePort);
  431.                 OpenDeskAcc(menuText);
  432.                 SetPort(savePort);
  433.             }
  434.             break;
  435.         case MENU_File:
  436. gQuitNow = TRUE;
  437.             switch (menuItem) {
  438.             case kFileQuit:
  439.                 gQuitNow = TRUE;
  440.                 break;
  441.             default:
  442.                 DebugStr("\pStrange File Menu");
  443.                 gQuitNow = TRUE;
  444.                 break;
  445.             }
  446.             break;
  447.         }
  448.         HiliteMenu(0);
  449. }
  450.  
  451.  
  452. OSErr
  453. DoBusReset(void)
  454. {
  455.  
  456.         OSErr                    status;
  457.         CntrlParam                pb;
  458.         
  459.         ShowString("\pIn DoBusReset");
  460.         CLEAR(pb);
  461.         pb.ioCRefNum = gDriverRefNum;
  462.         pb.csCode = kControlDoSCSIBusReset;
  463.         status = PBControlSync((ParmBlkPtr) &pb);
  464.         ShowStatusString(status, "\pDoBusReset PBControl status");
  465.         if (pb.ioResult != noErr) {
  466.             ShowDecimal(pb.ioResult, "\pBus reset failed");
  467.             gQuitNow = TRUE;
  468.         }
  469.         return (status);
  470. }
  471.  
  472. OSErr
  473. DoTestUnitReady(void)
  474. {
  475.         OSErr                    status;
  476.         SCSI_6_Byte_Command        testUnitReady;
  477.         char                    foo[64];
  478.         
  479.         CLEAR(testUnitReady);
  480.         testUnitReady.opcode = kScsiCmdTestUnitReady;
  481.         ShowString("\pCalling TestUnitReady");
  482.         status = DoNCRDriverIOWithSense(
  483.                     kNCRDriverNoDataPhase,
  484.                     gSCSITargetID,
  485.                     gSCSITargetLUN,
  486.                     (unsigned char *) &testUnitReady,
  487.                     (Ptr) foo,                /* PBRead must pass a real buffer    */
  488.                     64,
  489.                     NULL
  490.                 );
  491.         return (status);
  492. }
  493.  
  494. OSErr
  495. DoDeviceInquiry(void)
  496. {
  497.         OSErr                        status;
  498.         SCSI_6_Byte_Command            deviceInquiry;
  499.         SCSI_Inquiry_Data            inquiryData;
  500.         
  501.         CLEAR(deviceInquiry);
  502.         deviceInquiry.opcode = kScsiCmdInquiry;
  503.         deviceInquiry.len = sizeof inquiryData;
  504.         CLEAR(inquiryData);
  505.         ShowString("\pCalling DeviceInquiry");
  506.         status = DoNCRDriverIOWithSense(
  507.                     kNCRDriverInputAllowed,
  508.                     gSCSITargetID,
  509.                     gSCSITargetLUN,
  510.                     (unsigned char *) &deviceInquiry,
  511.                     (Ptr) &inquiryData,
  512.                     sizeof inquiryData,
  513.                     NULL
  514.                 );
  515.         if (status == noErr) {
  516.             inquiryData.vendor[-1] = 8 + 16 + 4;
  517.             ShowString(&inquiryData.vendor[-1]);
  518.         }
  519.         return (status);
  520. #undef INQUIRY
  521. }
  522.  
  523. OSErr
  524. DoReadCapacity(void)
  525. {
  526.         OSErr                        status;
  527.         SCSI_12_Byte_Command        capacityCmd;
  528.         struct SCSI_Capacity_Data     capacityData;
  529.  
  530.         ShowString("\pDoReadCapacity");
  531.         CLEAR(capacityCmd);
  532.         CLEAR(capacityData);
  533.         /*
  534.          * The 6-byte read command can read up to 128 blocks of data (1-127 reads that
  535.          * number of blocks, while zero reads 128 blocks). For more flexibility, you
  536.          * should use the 10-byte Read command.
  537.          */
  538.         capacityCmd.opcode = kScsiCmdReadCapacity;
  539.         status = DoNCRDriverIOWithSense(
  540.                     kNCRDriverInputAllowed,
  541.                     gSCSITargetID,
  542.                     gSCSITargetLUN,
  543.                     (unsigned char *) &capacityCmd,
  544.                     (Ptr) &capacityData,
  545.                     sizeof capacityData,
  546.                     NULL
  547.                 );
  548.         if (status == noErr) {
  549.             gDeviceSize =
  550.                 ( (capacityData.lbn4 << 24)
  551.                 | (capacityData.lbn3 << 16)
  552.                 | (capacityData.lbn2 <<  8)
  553.                 | (capacityData.lbn1      )
  554.                 );
  555.             gCurrentBlockLength =
  556.                 ( (capacityData.len4 << 24)
  557.                 | (capacityData.len3 << 16)
  558.                 | (capacityData.len2 <<  8)
  559.                 | (capacityData.len1      )
  560.                 );
  561.             WriteLogEntry(gLogRecordPtr, 'Test',
  562.                 LogFormat3(kLogFormatUnsigned, kLogFormatUnsigned, kLogFormatString),
  563.                 gDeviceSize, gCurrentBlockLength,
  564.                 "\pDevice Size, Block Length"
  565.             );
  566.         }
  567.         return (status);
  568. }
  569.  
  570. OSErr
  571. DoReadBlock(
  572.         unsigned long            blockNumber,
  573.         unsigned long            nBlocks,
  574.         Boolean                    dumpBlock
  575.     )
  576. {
  577.         OSErr                    status;
  578.         SCSI_6_Byte_Command        readData;
  579.         unsigned long            i;
  580.         Boolean                    readSomething;
  581.         unsigned long            transferLength;
  582.         unsigned long            actualTransferLength;
  583.  
  584.         transferLength = nBlocks * gCurrentBlockLength;
  585.         ClearMemory(gReadBufferPtr, transferLength);    
  586.         CLEAR(readData);    
  587.         /*
  588.          * The 6-byte read command can read up to 128 blocks of
  589.          * data (1-127 reads that number of blocks, while zero
  590.          * reads 128 blocks). For more flexibility, you should
  591.          * use the 10-byte Read command.
  592.          */
  593.         readData.opcode = kScsiCmdRead6;
  594.         readData.len = nBlocks;
  595.         if (readData.len == 0)    /* Zero means 128 */
  596.             readData.len = 1;
  597.         readData.lbn3 = (blockNumber >> 16) & 0xFF;
  598.         readData.lbn2 = (blockNumber >>  8) & 0xFF;
  599.         readData.lbn1 = (blockNumber >>  0) & 0xFF;
  600.         WriteLogEntry(gLogRecordPtr, 'Read',
  601.             LogFormat4(kLogFormatUnsigned, kLogFormatUnsigned,
  602.                 kLogFormatUnsigned, kLogFormatString),
  603.             blockNumber, nBlocks, transferLength,
  604.             "\pRead block"
  605.         );
  606.         status = DoNCRDriverIOWithSense(
  607.                     kNCRDriverInputAllowed,
  608.                     gSCSITargetID,
  609.                     gSCSITargetLUN,
  610.                     (unsigned char *) &readData,
  611.                     (Ptr) gReadBufferPtr,
  612.                     transferLength,
  613.                     &actualTransferLength
  614.                 );
  615.         readSomething = FALSE;
  616.         for (i = 0; i < actualTransferLength; i++) {
  617.             if (gReadBufferPtr[i] != 0) {
  618.                 readSomething = TRUE;
  619.                 break;
  620.             }
  621.         }
  622.         if (dumpBlock) {
  623.             if (readSomething == FALSE)
  624.                 ShowDecimal(blockNumber, "\pReadBlock is NULL after read");
  625.             else {
  626.                 ShowDecimal(blockNumber, "\pReadBlock results");
  627.                 ShowMemory((Ptr) gReadBufferPtr, actualTransferLength);
  628.             }
  629.         }
  630.         if (status == noErr && actualTransferLength != transferLength) {
  631.             ShowString("\pShort transfer");
  632.             status = ioErr;
  633.         }
  634.         return (status);
  635. }
  636.  
  637. OSErr
  638. DoNCRDriverIOWithSense(
  639.         unsigned short            driverAction,        /* Input, output, or nothing    */
  640.         unsigned short            targetID,            /* SCSI Bus ID                    */
  641.         unsigned short            targetLUN,            /* SCSI LUN -- not supported    */
  642.         const unsigned char        *scsiCommand,        /* SCSI Command itself            */
  643.         Ptr                        dataBufferPtr,        /* User data buffer or NULL     */
  644.         unsigned long            transferCount,
  645.         unsigned long            *actualTransferCount
  646.     )
  647. {
  648.         OSErr                    status;
  649.         Boolean                    retry;
  650.         short                    trials;
  651.         
  652.         retry = TRUE;
  653.         for (trials = 0; trials < 3 && retry; trials++) {
  654.             retry = FALSE;
  655.             status = DoNCRDriverIO(
  656.                         driverAction,
  657.                         targetID,
  658.                         targetLUN,
  659.                         scsiCommand,
  660.                         dataBufferPtr,
  661.                         transferCount,
  662.                         actualTransferCount
  663.                     );
  664.             if (status == scsiNonZeroStatus) {
  665.                 retry = DoRequestSense(targetID, targetLUN);
  666.                 if (retry)
  667.                     ShowString("\pRetry after Unit Attention");
  668.             }
  669.         };
  670.         return (status);
  671. }
  672.  
  673. /*
  674.  * Return TRUE if this is Unit Attention
  675.  */
  676. Boolean
  677. DoRequestSense(
  678.         unsigned short            targetID,            /* SCSI Bus ID                    */
  679.         unsigned short            targetLUN            /* SCSI LUN -- not supported    */
  680.     )
  681. {
  682.         OSErr                    status;
  683.         SCSI_6_Byte_Command        requestSense;
  684.         unsigned long            actualTransferCount;
  685.         SCSI_Sense_Data            sense;
  686.         Boolean                    result;
  687.         
  688.         CLEAR(requestSense);
  689.         CLEAR(sense);
  690.         requestSense.opcode = kScsiCmdRequestSense;
  691.         requestSense.len = sizeof sense;
  692.         status = DoNCRDriverIO(
  693.             kNCRDriverInputAllowed,
  694.             targetID,
  695.             targetLUN,
  696.             (unsigned char *) &requestSense,
  697.             (Ptr) &sense,
  698.             sizeof sense,
  699.             &actualTransferCount
  700.         );
  701.         result = ShowRequestSense(targetID, targetLUN, status, &sense, actualTransferCount);
  702.         return (result);
  703. }
  704.  
  705. /*
  706.  * DoNCRDriverIO returns kNCRNonZeroStatus if the target returned CheckCondition.
  707.  */
  708. OSErr
  709. DoNCRDriverIO(
  710.         unsigned short            driverAction,        /* Input, output, or nothing    */
  711.         unsigned short            targetID,            /* SCSI Bus ID                    */
  712.         unsigned short            targetLUN,            /* SCSI LUN -- not supported    */
  713.         const unsigned char        *scsiCommand,        /* SCSI Command itself            */
  714.         Ptr                        dataBufferPtr,        /* User data buffer or NULL     */
  715.         unsigned long            transferCount,
  716.         unsigned long            *actualTransferCount
  717.     )
  718. {
  719.         OSErr                    status;
  720. #define NCR    (gNCRSCSIParam)
  721. #define PB    (gIOParam)
  722.  
  723.         status = noErr;
  724.         /*
  725.          * Setup the parameter block
  726.          */
  727.         CLEAR(PB);
  728.         CLEAR(NCR);
  729.         PB.ioRefNum = gDriverRefNum;
  730.         NCR.driverAction = driverAction;
  731.         NCR.targetID = targetID;
  732.         NCR.logicalUnitNumber = targetLUN;
  733.         NCR.scsiCommandLength = SCSIGetCommandLength(scsiCommand);
  734.         BlockMove(scsiCommand, NCR.scsiCommand, NCR.scsiCommandLength);
  735.         NCR.watchdogTimeout = kTimeout;        /* Presumably a two second timeout    */
  736.         PB.ioMisc = (Ptr) &gNCRSCSIParam;
  737.         PB.ioBuffer = dataBufferPtr;
  738.         PB.ioReqCount = transferCount;
  739.         if ((driverAction & kNCRDriverOutputAllowed) != 0)
  740.             status = PBWriteSync((ParmBlkPtr) &PB);
  741.         else {
  742.             status = PBReadSync((ParmBlkPtr) &PB);
  743.         }
  744.         if (actualTransferCount != NULL)
  745.             *actualTransferCount = PB.ioActCount;
  746.         if (PB.ioResult == ioErr)
  747.             gQuitNow = TRUE;
  748.         WriteLogEntry(
  749.             gLogRecordPtr, 'DoIO',
  750.             LogFormat5(
  751.                 kLogFormatSigned, kLogFormatSigned, kLogFormatUnsigned,
  752.                 kLogFormatUnsigned, kLogFormatString
  753.             ),
  754.             (signed long) status,
  755.             (signed long) PB.ioResult,
  756.             transferCount,
  757.             PB.ioActCount,
  758.             "\pDoDriveriO"
  759.         );
  760.         if (PB.ioResult == scsiCommandTimeout) {
  761.             status = DoIORundown();
  762.             gQuitNow = TRUE;
  763.         }
  764.         return (status);
  765. #undef PB
  766. #undef NCR
  767. }
  768.  
  769. /*
  770.  * The timer fired and we may be stuck with the bus busy. Try to rundown I/O using
  771.  * our private Control call. For the time being, we don't attempt another timeout.
  772.  */
  773. OSErr
  774. DoIORundown(void)
  775. {
  776.         OSErr                    status;
  777.         
  778.         NCRDriverRundownParam    pb;
  779.         
  780.         CLEAR(pb);
  781.         pb.ioCRefNum = gDriverRefNum;
  782.         pb.csCode = kControlDoSCSIRundown;
  783.         pb.watchdogTimeout = kNoSCSITimeout;
  784.         ShowString("\pCalling DoIORundown");
  785.         status = PBControlSync((ParmBlkPtr) &pb);
  786.         ShowStatusString(status, "\pDoIORundown PBControl status");
  787.         gQuitNow = TRUE;
  788.         return (status);
  789. }
  790.  
  791. unsigned long
  792. SCSIGetCommandLength(
  793.         const unsigned char        *cmdBlock
  794.     )
  795. {
  796.         unsigned long            result;
  797.         /*
  798.          * Look at the "group code" in the command operation. Return a parameter
  799.          * error for the reserved (3, 4) and vendor-specific command (6, 7)
  800.          * command groups. Otherwise, set the command length from the group code
  801.          * value as specified in the SCSI-II spec. Then, copy the command block
  802.          * into the parameter block (this centralizes everything for debugging
  803.          * convenience).
  804.          */
  805.         switch (cmdBlock[0] & 0xE0) {
  806.         case (0 << 5):    result = 6;        break;
  807.         case (1 << 5):
  808.         case (2 << 5):    result = 10;    break;
  809.         case (5 << 5):    result = 12;    break;
  810.         default:        result = 0;        break;        /* This results in an error    */
  811.         }
  812.         return (result);
  813. }
  814.  
  815.  
  816. Boolean
  817. ShowRequestSense(
  818.         unsigned short            targetID,            /* SCSI Bus ID                    */
  819.         unsigned short            targetLUN,            /* SCSI LUN -- not supported    */
  820.         OSErr                    callStatus,            /* DoDriverIO Request Sense        */
  821.         const SCSI_Sense_Data    *sense,
  822.         unsigned long            actualTransferCount
  823.     )
  824. {
  825.         Boolean                    result;
  826.         Str255                    work;
  827.         static const StringPtr gSenseKeyText[] = {
  828.             "\p No error",
  829.             "\p Recovered error",
  830.             "\p Not ready",
  831.             "\p Medium error",
  832.             "\p Hardware error",
  833.             "\p Illegal request",
  834.             "\p Unit attention",
  835.             "\p Data protection",
  836.             "\p Blank check",    
  837.             "\p Vendor specific",
  838.             "\p Copy aborted",
  839.             "\p Command aborted",
  840.             "\p Compare equal",    
  841.             "\p Volume overflow",
  842.             "\p Miscompare",
  843.             "\p Reserved"
  844.         };
  845. #define SENSE (*sense)
  846.  
  847.         result = FALSE;
  848.         WriteLogEntry(gLogRecordPtr, 'Sens',
  849.             LogFormat5(kLogFormatUnsigned, kLogFormatUnsigned,
  850.                 kLogFormatSigned, kLogFormatUnsigned, kLogFormatString),
  851.             (UInt32) targetID, (UInt32) targetLUN, (SInt32) callStatus,
  852.             (UInt32) actualTransferCount,
  853.             "\pbus lun stat trans"
  854.         );
  855.         if (actualTransferCount != sizeof SENSE) {
  856.             WriteLogEntry(gLogRecordPtr, 'Sens',
  857.                 LogFormat3(kLogFormatUnsigned, kLogFormatUnsigned, kLogFormatString),
  858.                 (UInt32) actualTransferCount, (UInt32) sizeof SENSE,
  859.                 "\pRead, sizeof sense"
  860.             );
  861.         }
  862.         ShowMemory((Ptr) &SENSE, actualTransferCount);
  863.         PStrCopy(work, "\pSense: ");
  864.         AppendHexLeadingZeros(work, SENSE.errorCode, 2);
  865.         if ((SENSE.errorCode & kScsiSenseInfoMask) != kScsiSenseInfoValid)
  866.             PStrCat(work, "\p Invalid Sense");
  867.         else {
  868.             PStrCat(work, gSenseKeyText[SENSE.senseKey & kScsiSenseKeyMask]);
  869.             if ((SENSE.senseKey & kScsiSenseILI) != 0)
  870.                 PStrCat(work, "\p ILI");
  871.             if ((SENSE.senseKey & kScsiSenseEOM) != 0)
  872.                 PStrCat(work, "\p EOM");
  873.             if ((SENSE.senseKey & kScsiSenseFileMark) != 0)
  874.                 PStrCat(work, "\p EOF");
  875.             WriteLogEntry(gLogRecordPtr, 'Sens', kLogFormatString, work);
  876.             work[0] = 0;
  877.             AppendHexLeadingZeros(work, SENSE.additionalSenseCode & 0xFF, 2);
  878.             PStrCat(work, "\p ");
  879.             AppendHexLeadingZeros(work, SENSE.additionalSenseQualifier & 0xFF, 2);
  880.             PStrCat(work, "\p ASC ASQ");
  881.             result = ((SENSE.senseKey & kScsiSenseKeyMask) == kScsiSenseUnitAtn);
  882.         }
  883.         WriteLogEntry(gLogRecordPtr, 'Sens', kLogFormatString, work);
  884.         return (result);
  885. #undef SENSE
  886. }
  887.  
  888.  
  889. void
  890. ShowMemory(
  891.         const Ptr                memStart,
  892.         unsigned long            byteCount
  893.     )
  894. {
  895.         unsigned long            startIndex;
  896.         unsigned long            endIndex;
  897.         unsigned long            i;
  898.         Str255                    work;
  899.  
  900.         /*
  901.          * WriteLogEntry writes up to 40 characters. This is organized as
  902.          *    Start Index                3
  903.          *    4 groups of 4 bytes:    4 * (8 + 1)
  904.          *    One left over for good luck.
  905.          */
  906. if (byteCount > 32) byteCount = 32;
  907.         ShowDecimal(byteCount, "\pMemory Dump");
  908.         for (startIndex = 0; startIndex < byteCount; startIndex += 16) {
  909.             endIndex = startIndex + 16;
  910.             if (endIndex > byteCount)
  911.                 endIndex = byteCount;
  912.             work[0] = 0;
  913.             AppendHexLeadingZeros(work, startIndex, 3);
  914.             for (i = startIndex; i < endIndex; i++) {
  915.                 if ((i & 0x03) == 0x00)
  916.                     AppendChar(work, ' ');    
  917.                 AppendHexLeadingZeros(work, memStart[i], 2);
  918.             }
  919.             ShowString(work);
  920.         }
  921. }
  922.  
  923. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  924.  * Output a string of hex digits with leading zeros. May not move memory. Note that
  925.  * "digits" is the field width, not the number of hex bytes. Debug only.
  926.  */
  927. void
  928. AppendHexLeadingZeros(
  929.         StringPtr                result,
  930.         unsigned long            value,
  931.         short                    fieldWidth
  932.     )
  933. {
  934.         if (--fieldWidth > 0)
  935.             AppendHexLeadingZeros(result, value >> 4, fieldWidth);
  936.         value &= 0x0F;
  937.         AppendChar(result,
  938.                 (value < 10)
  939.                 ? value + '0'
  940.                 : (value + ('A' - 10))
  941.             );
  942. }
  943.  
  944. void
  945. AppendSigned(
  946.         StringPtr                result,
  947.         signed long                value
  948.     )
  949. {
  950.         if (value < 0) {
  951.             AppendChar(result, '-');
  952.             value = (-value);
  953.         }
  954.         AppendUnsigned(result, (unsigned long) value);
  955. }
  956.  
  957. void
  958. AppendUnsigned(
  959.         StringPtr                result,
  960.         unsigned long            value
  961.     )
  962. {
  963.         if (value >= 10)
  964.             AppendUnsigned(result, value / 10);
  965.         AppendChar(result, (value % 10) + '0');
  966. }
  967.  
  968. void
  969. ClearMemory(
  970.         register Ptr            memPtr,
  971.         register Size            memSize
  972.     )
  973. {
  974.         while (memSize > 0) {
  975.             *memPtr++ = 0;
  976.             --memSize;
  977.         }
  978. }
  979.